-- TO look at:
-- https://github.com/romockee/powerarrow
-- If LuaRocks is installed, make sure that packages installed through it are
-- found (e.g. lgi). If LuaRocks is not installed, do nothing.
local luarocks_success = pcall(require, 'luarocks.loader')

local awesome = assert(awesome)
local client = assert(client)
local root = assert(root)
local screen = assert(screen)

local log = require('log')

log.info("=================================================================================")
log.info("Starting new session")
log.info("=================================================================================")

require('use_copycat')
require('setup')

log.info("Package Path   : %s", package.path)
log.info("LuaRocks Loaded: %s", luarocks_success)

-- TODO: Figure out environment variables for more portable config
-- local submodule_dir = os.getenv("XDG_CONFIG_HOME") .. '/submodules/'
local submodule_dir = '/home/tj/.config/submodules/'

-- Standard awesome library
local gears = require('gears')
local awful = require('awful')
require('awful.autofocus')
-- Widget and layout library
local wibox = require('wibox')
-- Theme handling library
local beautiful = require('beautiful')
-- Notification library
local naughty = require('naughty')
local menubar = require('menubar')
local hotkeys_popup = require('awful.hotkeys_popup')

-- Enable hotkeys help widget for VIM and other apps
-- when client with a matching name is opened:
require('awful.hotkeys_popup.keys')

-- Load Debian menu entries
local debian = require('debian.menu')
local has_fdo, freedesktop = pcall(require, 'freedesktop')

-- {{{ Error handling
-- Check if awesome encountered an error during startup and fell back to
-- another config (This code will only ever execute for the fallback config)
if awesome.startup_errors then
  naughty.notify(
    {
      preset = naughty.config.presets.critical,
      title = 'Oops, there were errors during startup!',
      text = awesome.startup_errors,
    }
  )
end

-- Handle runtime errors after startup
do
  local in_error = false
  awesome.connect_signal(
    'debug::error', function(err)
      -- Make sure we don't go into an endless error loop
      if in_error then return end
      in_error = true

      naughty.notify(
        {
          preset = naughty.config.presets.critical,
          title = 'Oops, an error happened!',
          text = tostring(err),
        }
      )
      in_error = false
    end
  )
end
-- }}}

-- {{{ Variable definitions
-- Themes define colours, icons, font and wallpapers.
local theme_ok = pcall(
  function()
    local powerarrow_theme = submodule_dir .. 'powerarrow/themes/powerarrow/theme.lua'

    log.info("Reading from %s...", powerarrow_theme)
    beautiful.init(powerarrow_theme)
    log.info("... Success")

    naughty.config.default_preset.font = beautiful.notify_font
    naughty.config.default_preset.fg = beautiful.notify_fg
    naughty.config.default_preset.bg = beautiful.notify_bg
    naughty.config.presets.normal.border_color = beautiful.notify_border
    naughty.config.presets.normal.opacity = 0.8
    naughty.config.presets.low.opacity = 0.8
    naughty.config.presets.critical.opacity = 0.8
  end
)
if not theme_ok then beautiful.init(gears.filesystem.get_themes_dir() .. 'default/theme.lua') end

-- This is used later as the default terminal and editor to run.
local terminal = 'kitty'
local editor = 'nvim'
local editor_cmd = terminal .. ' -e ' .. editor

local mods = {
  super = 'Mod4',
  shift = 'Shift',
  control = 'Control',
}

-- Table of layouts to cover with awful.layout.inc, order matters.
awful.layout.layouts = {
  awful.layout.suit.floating,
  awful.layout.suit.tile,
  -- awful.layout.suit.tile.left,
  -- awful.layout.suit.tile.bottom,
  -- awful.layout.suit.tile.top,
  -- awful.layout.suit.fair,
  awful.layout.suit.fair.horizontal,
  -- awful.layout.suit.spiral,
  awful.layout.suit.spiral.dwindle,
  -- awful.layout.suit.max,
  -- awful.layout.suit.max.fullscreen,
  awful.layout.suit.magnifier,
  -- awful.layout.suit.corner.nw,
  -- awful.layout.suit.corner.ne,
  -- awful.layout.suit.corner.sw,
  -- awful.layout.suit.corner.se,
}
-- }}}

-- {{{ Menu
-- Create a launcher widget and a main menu
local my_awesome_menu = {
  {
    'hotkeys',
    function() hotkeys_popup.show_help(nil, awful.screen.focused()) end,
  },
  {'manual', terminal .. ' -e man awesome'},
  {'edit config', editor_cmd .. ' ' .. awesome.conffile},
  {'restart', awesome.restart},
  {'quit', function() awesome.quit() end},
}

local menu_awesome = {'awesome', my_awesome_menu, beautiful.awesome_icon}
local menu_terminal = {'open terminal', terminal}

local my_main_menu
if has_fdo then
  my_main_menu = freedesktop.menu.build(
    {before = {menu_awesome}, after = {menu_terminal}}
  )
else
  my_main_menu = awful.menu(
    {
      items = {
        menu_awesome,
        {'Debian', debian.menu.Debian_menu.Debian},
        menu_terminal,
      },
    }
  )
end

mylauncher = awful.widget.launcher(
  {image = beautiful.awesome_icon, menu = my_main_menu}
)

-- Menubar configuration
menubar.utils.terminal = terminal -- Set the terminal for applications that require it
-- }}}

-- Keyboard map indicator and switcher
mykeyboardlayout = awful.widget.keyboardlayout()

-- {{{ Wibar
-- Create a textclock widget
mytextclock = wibox.widget.textclock()

-- Create a wibox for each screen and add it
local taglist_buttons = gears.table.join(
  awful.button({}, 1, function(t) t:view_only() end),
  awful.button({mods.super}, 1, function(t) if client.focus then client.focus:move_to_tag(t) end end),
  awful.button({}, 3, awful.tag.viewtoggle),
  awful.button({mods.super}, 3, function(t) if client.focus then client.focus:toggle_tag(t) end end),
  awful.button({}, 4, function(t) awful.tag.viewnext(t.screen) end),
  awful.button({}, 5, function(t) awful.tag.viewprev(t.screen) end)
)

local tasklist_buttons = gears.table.join(
  awful.button(
    {}, 1, function(c)
      if c == client.focus then
        c.minimized = true
      else
        c:emit_signal('request::activate', 'tasklist', {raise = true})
      end
    end
  ), awful.button(
    {}, 3, function() awful.menu.client_list({theme = {width = 250}}) end
  ), awful.button({}, 4, function() awful.client.focus.byidx(1) end),
  awful.button({}, 5, function() awful.client.focus.byidx(-1) end)
)

local function set_wallpaper(s)
  -- Wallpaper
  if beautiful.wallpaper then
    local wallpaper = beautiful.wallpaper
    -- If wallpaper is a function, call it with the screen
    if type(wallpaper) == 'function' then wallpaper = wallpaper(s) end
    gears.wallpaper.maximized(wallpaper, s, true)
  end
end

-- Re-set wallpaper when a screen's geometry changes (e.g. different resolution)
screen.connect_signal('property::geometry', set_wallpaper)

awful.screen.connect_for_each_screen(
  function(s)
    -- Wallpaper
    set_wallpaper(s)

    -- Each screen has its own tag table.
    awful.tag({'1', '2', '3', '4', '5', '6', '7', '8', '9'}, s, awful.layout.layouts[1])

    -- Create a promptbox for each screen
    s.mypromptbox = awful.widget.prompt()

    -- Create an imagebox widget which will contain an icon indicating which layout we're using.
    -- We need one layoutbox per screen.
    s.mylayoutbox = awful.widget.layoutbox(s)
    s.mylayoutbox:buttons(
      gears.table.join(
        awful.button({}, 1, function() awful.layout.inc(1) end),
        awful.button({}, 3, function() awful.layout.inc(-1) end),
        awful.button({}, 4, function() awful.layout.inc(1) end),
        awful.button({}, 5, function() awful.layout.inc(-1) end)
      )
    )
    -- Create a taglist widget
    s.mytaglist = awful.widget.taglist {
      screen = s,
      filter = awful.widget.taglist.filter.all,
      buttons = taglist_buttons,
    }

    -- Create a tasklist widget
    s.mytasklist = awful.widget.tasklist {
      screen = s,
      filter = awful.widget.tasklist.filter.currenttags,
      buttons = tasklist_buttons,
    }

    -- Create the wibox
    s.mywibox = awful.wibar({position = 'top', screen = s})

    -- Add widgets to the wibox
    s.mywibox:setup{
      layout = wibox.layout.align.horizontal,
      { -- Left widgets
        layout = wibox.layout.fixed.horizontal,
        mylauncher,
        s.mytaglist,
        s.mypromptbox,
      },
      s.mytasklist, -- Middle widget
      { -- Right widgets
        layout = wibox.layout.fixed.horizontal,
        mykeyboardlayout,
        wibox.widget.systray(),
        mytextclock,
        s.mylayoutbox,
      },
    }
  end
)
-- }}}

-- {{{ Mouse bindings
root.buttons(
  gears.table.join(
    awful.button({}, 3, function() my_main_menu:toggle() end), awful.button({}, 4, awful.tag.viewnext),
    awful.button({}, 5, awful.tag.viewprev)
  )
)
-- }}}

-- {{{ Key bindings
local globalkeys = gears.table.join(
  -- Keys to potentially remap
  awful.key(
    {mods.super}, 'Right', awful.tag.viewnext, {
      description = 'view next',
      group = 'tag',
    }
  ), awful.key(
    {mods.super}, 'Left', awful.tag.viewprev, {
      description = 'view previous',
      group = 'tag',
    }
  ),
  --
  --
  awful.key(
    {mods.super}, 's', hotkeys_popup.show_help, {
      description = 'show help',
      group = 'awesome',
    }
  ),awful.key(
    {mods.super}, 'Escape', awful.tag.history.restore, {
      description = 'go back',
      group = 'tag',
    }
  ), awful.key(
    {mods.super}, 'j', function() awful.client.focus.byidx(1) end, {
      description = 'focus next by index',
      group = 'client',
    }
  ), awful.key(
    {mods.super}, 'k', function() awful.client.focus.byidx(-1) end,
    {description = 'focus previous by index', group = 'client'}
  ), awful.key(
    {mods.super}, 'w', function() my_main_menu:show() end, {
      description = 'show main menu',
      group = 'awesome',
    }
  ), -- Layout manipulation
  awful.key(
    {mods.super, mods.shift}, 'j', function() awful.client.swap.byidx(1) end,
    {description = 'swap with next client by index', group = 'client'}
  ), awful.key(
    {mods.super, mods.shift}, 'k', function() awful.client.swap.byidx(-1) end,
    {description = 'swap with previous client by index', group = 'client'}
  ), awful.key(
    {mods.super, mods.control}, 'j', function() awful.screen.focus_relative(1) end,
    {description = 'focus the next screen', group = 'screen'}
  ), awful.key(
    {mods.super, mods.control}, 'k', function() awful.screen.focus_relative(-1) end,
    {description = 'focus the previous screen', group = 'screen'}
  ), awful.key(
    {mods.super}, 'u', awful.client.urgent.jumpto, {
      description = 'jump to urgent client',
      group = 'client',
    }
  ), awful.key(
    {mods.super}, 'Tab', function()
      awful.client.focus.history.previous()
      if client.focus then client.focus:raise() end
    end, {description = 'go back', group = 'client'}
  ), -- Standard program
  awful.key(
    {mods.super}, 'Return', function() awful.spawn(terminal) end, {
      description = 'open a terminal',
      group = 'launcher',
    }
  ), awful.key(
    {mods.super, mods.control}, 'r', awesome.restart, {
      description = 'reload awesome',
      group = 'awesome',
    }
  ), awful.key(
    {mods.super, mods.shift}, 'q', awesome.quit, {
      description = 'quit awesome',
      group = 'awesome',
    }
  ), awful.key(
    {mods.super}, 'l', function() awful.tag.incmwfact(0.05) end,
    {description = 'increase master width factor', group = 'layout'}
  ), awful.key(
    {mods.super}, 'h', function() awful.tag.incmwfact(-0.05) end,
    {description = 'decrease master width factor', group = 'layout'}
  ), awful.key(
    {mods.super, mods.shift}, 'h', function() awful.tag.incnmaster(1, nil, true) end,
    {description = 'increase the number of master clients', group = 'layout'}
  ), awful.key(
    {mods.super, mods.shift}, 'l', function() awful.tag.incnmaster(-1, nil, true) end,
    {description = 'decrease the number of master clients', group = 'layout'}
  ), awful.key(
    {mods.super, mods.control}, 'h', function() awful.tag.incncol(1, nil, true) end,
    {description = 'increase the number of columns', group = 'layout'}
  ), awful.key(
    {mods.super, mods.control}, 'l', function() awful.tag.incncol(-1, nil, true) end,
    {description = 'decrease the number of columns', group = 'layout'}
  ), awful.key(
    {mods.super}, 'space', function() awful.layout.inc(1) end, {
      description = 'select next',
      group = 'layout',
    }
  ), awful.key(
    {mods.super, mods.shift}, 'space', function() awful.layout.inc(-1) end,
    {description = 'select previous', group = 'layout'}
  ), awful.key(
    {mods.super, mods.control}, 'n', function()
      local c = awful.client.restore()
      -- Focus restored client
      if c then c:emit_signal(
        'request::activate', 'key.unminimize', {raise = true}
      ) end
    end, {description = 'restore minimized', group = 'client'}
  ), -- Prompt
  awful.key(
    {mods.super}, 'r', function() awful.screen.focused().mypromptbox:run() end,
    {description = 'run prompt', group = 'launcher'}
  ), awful.key(
    {mods.super}, 'x', function()
      awful.prompt.run {
        prompt = 'Run Lua code: ',
        textbox = awful.screen.focused().mypromptbox.widget,
        exe_callback = awful.util.eval,
        history_path = awful.util.get_cache_dir() .. '/history_eval',
      }
    end, {description = 'lua execute prompt', group = 'awesome'}
  ), -- Menubar
  awful.key(
    {mods.super}, 'p', function() menubar.show() end, {
      description = 'show the menubar',
      group = 'launcher',
    }
  ),

  awful.key({}, "#122", function() awful.util.spawn("amixer set Master 2-") end),
  awful.key({}, "#123", function() awful.util.spawn("amixer set Master 2+") end)
)

clientkeys = gears.table.join(
  awful.key(
    {mods.super}, 'f', function(c)
      c.fullscreen = not c.fullscreen
      c:raise()
    end, {description = 'toggle fullscreen', group = 'client'}
  ), awful.key(
    {mods.super, mods.shift}, 'c', function(c) c:kill() end, {
      description = 'close',
      group = 'client',
    }
  ), awful.key(
    {mods.super, mods.control}, 'space', awful.client.floating.toggle, {
      description = 'toggle floating',
      group = 'client',
    }
  ), awful.key(
    {mods.super, mods.control}, 'Return', function(c) c:swap(awful.client.getmaster()) end,
    {description = 'move to master', group = 'client'}
  ), awful.key(
    {mods.super}, 'o', function(c) c:move_to_screen() end, {
      description = 'move to screen',
      group = 'client',
    }
  ), awful.key(
    {mods.super}, 't', function(c) c.ontop = not c.ontop end, {
      description = 'toggle keep on top',
      group = 'client',
    }
  ), awful.key(
    {mods.super}, 'n', function(c)
      -- The client currently has the input focus, so it cannot be
      -- minimized, since minimized clients can't have the focus.
      c.minimized = true
    end, {description = 'minimize', group = 'client'}
  ), awful.key(
    {mods.super}, 'm', function(c)
      c.maximized = not c.maximized
      c:raise()
    end, {description = '(un)maximize', group = 'client'}
  ), awful.key(
    {mods.super, mods.control}, 'm', function(c)
      c.maximized_vertical = not c.maximized_vertical
      c:raise()
    end, {description = '(un)maximize vertically', group = 'client'}
  ), awful.key(
    {mods.super, mods.shift}, 'm', function(c)
      c.maximized_horizontal = not c.maximized_horizontal
      c:raise()
    end, {description = '(un)maximize horizontally', group = 'client'}
  )
)

-- Bind all key numbers to tags.
-- Be careful: we use keycodes to make it work on any keyboard layout.
-- This should map on the top row of your keyboard, usually 1 to 9.
for i = 1, 9 do
  globalkeys = gears.table.join(
    globalkeys, -- View tag only.
    awful.key(
      {mods.super}, '#' .. i + 9, function()
        local screen = awful.screen.focused()
        local tag = screen.tags[i]
        if tag then tag:view_only() end
      end, {description = 'view tag #' .. i, group = 'tag'}
    ), -- Toggle tag display.
    awful.key(
      {mods.super, mods.control}, '#' .. i + 9, function()
        local screen = awful.screen.focused()
        local tag = screen.tags[i]
        if tag then awful.tag.viewtoggle(tag) end
      end, {description = 'toggle tag #' .. i, group = 'tag'}
    ), -- Move client to tag.
    awful.key(
      {mods.super, mods.shift}, '#' .. i + 9, function()
        if client.focus then
          local tag = client.focus.screen.tags[i]
          if tag then client.focus:move_to_tag(tag) end
        end
      end, {description = 'move focused client to tag #' .. i, group = 'tag'}
    ), -- Toggle tag on focused client.
    awful.key(
      {mods.super, mods.control, mods.shift}, '#' .. i + 9, function()
        if client.focus then
          local tag = client.focus.screen.tags[i]
          if tag then client.focus:toggle_tag(tag) end
        end
      end, {description = 'toggle focused client on tag #' .. i, group = 'tag'}
    )
  )
end

clientbuttons = gears.table.join(
  awful.button(
    {}, 1, function(c) c:emit_signal(
      'request::activate', 'mouse_click', {raise = true}
    ) end
  ), awful.button(
    {mods.super}, 1, function(c)
      c:emit_signal('request::activate', 'mouse_click', {raise = true})
      awful.mouse.client.move(c)
    end
  ), awful.button(
    {mods.super}, 3, function(c)
      c:emit_signal('request::activate', 'mouse_click', {raise = true})
      awful.mouse.client.resize(c)
    end
  )
)

-- Set keys
root.keys(globalkeys)
-- }}}

-- {{{ Rules
-- Rules to apply to new clients (through the "manage" signal).
awful.rules.rules = {
  -- All clients will match this rule.
  {
    rule = {},
    properties = {
      border_width = beautiful.border_width,
      border_color = beautiful.border_normal,
      focus = awful.client.focus.filter,
      raise = true,
      keys = clientkeys,
      buttons = clientbuttons,
      screen = awful.screen.preferred,
      placement = awful.placement.no_overlap + awful.placement.no_offscreen,
    },
  }, -- Floating clients.
  {
    rule_any = {
      instance = {
        'DTA', -- Firefox addon DownThemAll.
        'copyq', -- Includes session name in class.
        'pinentry',
      },
      class = {
        'Arandr',
        'Blueman-manager',
        'Gpick',
        'Kruler',
        'MessageWin', -- kalarm.
        'Sxiv',
        'Tor Browser', -- Needs a fixed window size to avoid fingerprinting by screen size.
        'Wpa_gui',
        'veromix',
        'xtightvncviewer',
      },

      -- Note that the name property shown in xprop might be set slightly after creation of the client
      -- and the name shown there might not match defined rules here.
      name = {
        'Event Tester', -- xev.
      },
      role = {
        'AlarmWindow', -- Thunderbird's calendar.
        'ConfigManager', -- Thunderbird's about:config.
        'pop-up', -- e.g. Google Chrome's (detached) Developer Tools.
      },
    },
    properties = {floating = true},
  }, -- Add titlebars to normal clients and dialogs
  {
    rule_any = {type = {'normal', 'dialog'}},
    properties = {titlebars_enabled = true},
  },

  -- Set Firefox to always map on the tag named "2" on screen 1.
  -- { rule = { class = "Firefox" },
  --   properties = { screen = 1, tag = "2" } },
}
-- }}}

-- {{{ Signals
-- Signal function to execute when a new client appears.
client.connect_signal(
  'manage', function(c)
    -- Set the windows at the slave,
    -- i.e. put it at the end of others instead of setting it master.
    -- if not awesome.startup then awful.client.setslave(c) end

    if awesome.startup and not c.size_hints.user_position and not c.size_hints.program_position then
      -- Prevent clients from being unreachable after screen count changes.
      awful.placement.no_offscreen(c)
    end
  end
)

-- Add a titlebar if titlebars_enabled is set to true in the rules.
client.connect_signal(
  'request::titlebars', function(c)
    -- buttons for the titlebar
    local buttons = gears.table.join(
      awful.button(
        {}, 1, function()
          c:emit_signal('request::activate', 'titlebar', {raise = true})
          awful.mouse.client.move(c)
        end
      ), awful.button(
        {}, 3, function()
          c:emit_signal('request::activate', 'titlebar', {raise = true})
          awful.mouse.client.resize(c)
        end
      )
    )

    awful.titlebar(c):setup{
      { -- Left
        awful.titlebar.widget.iconwidget(c),
        buttons = buttons,
        layout = wibox.layout.fixed.horizontal,
      },
      { -- Middle
        { -- Title
          align = 'center',
          widget = awful.titlebar.widget.titlewidget(c),
        },
        buttons = buttons,
        layout = wibox.layout.flex.horizontal,
      },
      { -- Right
        awful.titlebar.widget.floatingbutton(c),
        awful.titlebar.widget.maximizedbutton(c),
        awful.titlebar.widget.stickybutton(c),
        awful.titlebar.widget.ontopbutton(c),
        awful.titlebar.widget.closebutton(c),
        layout = wibox.layout.fixed.horizontal(),
      },
      layout = wibox.layout.align.horizontal,
    }
  end
)

-- Enable sloppy focus, so that focus follows mouse.
client.connect_signal(
  'mouse::enter', function(c) c:emit_signal(
    'request::activate', 'mouse_enter', {raise = false}
  ) end
)

client.connect_signal('focus', function(c) c.border_color = beautiful.border_focus or '' end)
client.connect_signal('unfocus', function(c) c.border_color = beautiful.border_normal or '' end)
-- }}}

-- TODO: Figure out what we want to do with these things.
-- local net_widgets = require("net_widgets")
-- net_wireless = net_widgets.wireless({interface="wlp1s0"})
-- Run my startup shell things
-- awful.util.spawn_with_shell("~/.config/awesome/autorun.sh")
